﻿using Ascend;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Media3D;

namespace Demo03_MouseEvents
{
	/// <summary>
	/// Interaction logic for MainWindow.xaml
	/// </summary>
	public partial class MainWindow : Window
	{
		#region Private Members
		// A gem's default brush
		private readonly SolidColorBrush GEM_BRUSH =
			new SolidColorBrush((Color)ColorConverter.ConvertFromString("#FFC4E700"));

		// To keep track of last node when mouse is pressed down
		private SceneNode _lastNodeOnDown;
		#endregion

		#region Lifecycle
		public MainWindow()
		{
			InitializeComponent();

			// Set data context for bindings
			DataContext = this;

			// Create our Scene3D object
			Scene = new Scene3D();

			// Setup the camera
			Scene.Camera.Position = new Point3D(6, 5, 8);
			Scene.Camera.LookDirection = new Vector3D(-6, -3, -8);

			// Load hierarchical gem model
			//
			// The model file must be accessible at runtime, so
			// the file contains a property specifying that it
			// be copied to the output directory when the project
			// is built (right-click 'gems.asc' in solution -> Properties).
			ModelSceneNode gems = Loader.Load<ModelSceneNode>("Models\\gems.asc");
			gems.Parent = Scene.RenderRoot;
			SetupMouseEvents(gems);

			// Now add a light, so objects can be seen
			LightSceneNode light =
				LightSceneNode.Create(new DirectionalLight(Colors.White, new Vector3D(0, -1, -2)));
			light.Parent = Scene.RenderRoot;
		}
		#endregion

		#region Public Properties
		/// <summary>
		/// Gets the Scene3D that is rendered.
		/// </summary>
		public Scene3D Scene
		{
			get;
			private set;
		}
		#endregion

		#region Private Methods
		/// <summary>
		/// Sets up mouse events for gem model
		/// </summary>
		/// <param name="node"></param>
		private void SetupMouseEvents(ModelSceneNode gemNode)
		{
			// Add handlers for all SceneNodes in hierarchy
			List<ModelSceneNode> nodes = gemNode.GetDescendants().OfType<ModelSceneNode>().ToList();
			nodes.Add(gemNode);

			foreach (ModelSceneNode n in nodes)
			{
				// MouseEnter and MouseLeave are direct events
				n.MouseEnter += Node_MouseEnter;
				n.MouseLeave += Node_MouseLeave;

				// All other mouse events are routed.
				// There are both bubbling and tunneling types.
				// Tunneling events have the word "Preview" prepending the name.
				n.PreviewMouseDown += Node_PreviewMouseDown;
				n.PreviewMouseUp += Node_PreviewMouseUp;

				// Bubbling mouse events.
				// Bubbling events are always evaluated after tunneling
				// counterparts. Handling any event (setting e.Handled = true)
				// stops routing.
				n.MouseDown += Node_MouseDown;
				n.MouseUp += Node_MouseUp;
			}
		}
		#endregion

		#region Event Handlers
		private void Node_MouseEnter(object sender, SceneNodeMouseEventArgs e)
		{
			ModelSceneNode node = sender as ModelSceneNode;

			// Make node shiny
			MaterialGroup gp = new MaterialGroup();
			gp.Children.Add(new DiffuseMaterial(GEM_BRUSH));
			gp.Children.Add(new SpecularMaterial(new SolidColorBrush(Colors.HotPink), 5));
			node.Model.Material = gp;
		}

		private void Node_MouseLeave(object sender, SceneNodeMouseEventArgs e)
		{
			ModelSceneNode node = sender as ModelSceneNode;

			// Make node diffuse again
			node.Model.Material = new DiffuseMaterial(GEM_BRUSH);
		}

		private void Node_PreviewMouseDown(object sender, SceneNodeMouseButtonEventArgs e)
		{
			ModelSceneNode node = sender as ModelSceneNode;

			// Change node color
			node.Model.Material = new DiffuseMaterial(new SolidColorBrush(Colors.HotPink));
		}

		private void Node_PreviewMouseUp(object sender, SceneNodeMouseButtonEventArgs e)
		{
			ModelSceneNode node = sender as ModelSceneNode;

			// Revert node color
			node.Model.Material = new DiffuseMaterial(GEM_BRUSH);
		}

		private void Node_MouseDown(object sender, SceneNodeMouseButtonEventArgs e)
		{
			// Set this node as the one being clicked
			_lastNodeOnDown = sender as SceneNode;

			// Stop routing so ancestors are not set as node being clicked
			e.Handled = true;
		}

		private void Node_MouseUp(object sender, SceneNodeMouseButtonEventArgs e)
		{
			if (sender != _lastNodeOnDown)
			{
				return;
			}

			// Look at node
			Scene.LookAt(sender as SceneNode);

			// Stop routing so ancestors are not looked at
			e.Handled = true;
		}
		#endregion
	}
}
